/*
 * $Id$
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package cn.calm.osgi.admin.bundle.actions;

import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletContext;

import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.osgi.BundleAccessor;
import org.apache.struts2.osgi.OsgiHost;
import org.apache.struts2.osgi.StrutsOsgiListener;
import org.apache.struts2.osgi.interceptor.ServiceAware;
import org.apache.struts2.util.ServletContextAware;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;

import cn.calm.osgi.admin.bundle.entity.BundleInfo;
import cn.calm.osgi.admin.bundle.service.IBundleService;
import cn.calm.osgi.admin.bundle.util.BundleUtil;
import cn.calm.osgi.admin.bundle.util.FileUtils;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.PackageConfig;
import com.opensymphony.xwork2.inject.Inject;

public class BundlesAction extends ActionSupport implements
		ServletContextAware, ServiceAware<IBundleService> {
	private static final Logger log=Logger.getLogger(BundlesAction.class);
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String symbolicName;
	private String version;
	
	private BundleAccessor bundleAccessor;
	private Configuration configuration;
	private OsgiHost osgiHost;
	private IBundleService service ;
	private File file;
	private String fileFileName;
	private BundleInfo info;

	public BundlesAction() {
	}

	public String index() {
		return SUCCESS;
	}

	public String view() {
		return SUCCESS;
	}

	public String upload() {
		log.debug("upload start");
		if (file == null) {
			return "upload";
		}
		String path = ServletActionContext.getServletContext().getRealPath(
				"/uploadTemp/");
		File bundleFile=new File(path+File.separator+fileFileName);
		try {
			FileUtils.copyFile(file, bundleFile);
			info = BundleUtil.readBundleInfo(bundleFile);
		} catch (Exception e) {
			addActionError(e.getMessage());
			return INPUT;
		}
		log.debug("upload end");
		return "detail";
	}
	
	public String add(){
		log.debug("add start");
		try {
			String path = ServletActionContext.getServletContext().getRealPath(
					"/uploadTemp/");
			File bundleFile=new File(path+File.separator+info.getBundleFilename());
			byte[] buf= FileUtils.fileToBytes(bundleFile);
			info.setContent(buf);
			service.addBundle(info);
		} catch (Exception e) {
			addActionError(e.getMessage());
			return INPUT;
		}
		log.debug("add end");
		return SUCCESS;
	}
	
	public String delete(){
		log.debug("delete start");
		service.deleteBundle(symbolicName,version);
		log.debug("delete end");
		return "index";
	}
	
	public String start() throws BundleException {
		log.debug("starting");
		Bundle bundle = osgiHost.getBundles().get(symbolicName);
		try {
			bundle.start();

			// start() fires a BundleEvent.STARTED, which loads the config
			// we need to wait until the config is loaded from that bundle but
			// there no easy way/elegant way to know if the bundle was processed
			// already
			Thread.sleep(1000);
		} catch (Exception e) {
			addActionError(e.toString());
			return "index";
		}
		log.debug("started");
		return view();
	}

	public String stop() throws BundleException {
		log.debug("stopping");
		Bundle bundle = osgiHost.getBundles().get(symbolicName);
		try {
			bundle.stop();
		} catch (Exception e) {
			addActionError(e.toString());
			return "index";
		}
		log.debug("stopped");
		return view();
	}

	public String update() throws BundleException {
		log.debug("updating");
		Bundle bundle = osgiHost.getBundles().get(symbolicName);
		try {
			bundle.update();
		} catch (Exception e) {
			addActionError(e.toString());
			return "index";
		}
		log.debug("updated");
		return view();
	}
	public String uninstall() throws BundleException {
		log.debug("uninstall start");
		Bundle bundle = osgiHost.getBundles().get(symbolicName);
		try {
			bundle.uninstall();
		} catch (Exception e) {
			addActionError(e.toString());
		}
		log.debug("uninstall end");
		return "index";
	}
	public boolean isStrutsEnabled(Bundle bundle) {
		return "true".equalsIgnoreCase((String) bundle.getHeaders().get(
				OsgiHost.OSGI_HEADER_STRUTS_ENABLED));
	}

	public Bundle getBundle() {
		return osgiHost.getBundles().get(symbolicName);
	}

	public List<PackageConfig> getPackages() {
		List<PackageConfig> pkgs = new ArrayList<PackageConfig>();
		Bundle bundle = getBundle();
		if (bundle.getState() == Bundle.ACTIVE) {
			for (String name : bundleAccessor.getPackagesByBundle(bundle)) {
				PackageConfig packageConfig = configuration
						.getPackageConfig(name);
				if (packageConfig != null)
					pkgs.add(packageConfig);
			}
		}
		return pkgs;
	}

	public ArrayList<String> getHeaderKeys() {
		return Collections.list(getBundle().getHeaders().keys());
	}

	public Set<BundleInfo> getBundles() {
		Set<BundleInfo> bundles = new TreeSet<BundleInfo>(
				new Comparator<BundleInfo>() {
					public int compare(BundleInfo bundle1, BundleInfo bundle2) {

						if ((bundle1.getStruts2Enabled() && bundle2
								.getStruts2Enabled())
								|| (!bundle1.getStruts2Enabled() && !bundle2
										.getStruts2Enabled()))
							return bundle1.getSymbolicName().compareTo(
									bundle2.getSymbolicName());
						else {
							return bundle1.getStruts2Enabled() ? -1 : 1;
						}
					}
				});

		Set<BundleInfo> infos = new HashSet<BundleInfo>();
		BundleInfo info;
		for (Bundle b : osgiHost.getBundles().values()) {
			info = new BundleInfo();
			info.setState(b.getState());
			info.setStruts2Enabled(isStrutsEnabled(b));
			info.setSymbolicName(b.getSymbolicName());
			info.setVersion(b.getVersion().toString());
			infos.add(info);
		}
		List<BundleInfo> list = service.listAll();
		infos.addAll(list);
		bundles.addAll(infos);
		return bundles;
	}

	public String displayProperty(Object obj) {
		if (obj.getClass().isArray()) {
			return Arrays.asList((Object[]) obj).toString();
		} else {
			return obj.toString();
		}
	}

	public String getBundleState(BundleInfo bundle) {
		switch (bundle.getState()) {
		case Bundle.ACTIVE:
			return "Active";
		case Bundle.INSTALLED:
			return "Installed";
		case Bundle.RESOLVED:
			return "Resolved";
		case Bundle.STARTING:
			return "Starting";
		case Bundle.STOPPING:
			return "Stopping";
		case Bundle.UNINSTALLED:
			return "Uninstalled";
		case 0:
			return "Not Install";
		default:
			throw new IllegalStateException("Invalid state");
		}
	}

	public String getBundleState(Bundle bundle) {
		switch (bundle.getState()) {
		case Bundle.ACTIVE:
			return "Active";
		case Bundle.INSTALLED:
			return "Installed";
		case Bundle.RESOLVED:
			return "Resolved";
		case Bundle.STARTING:
			return "Starting";
		case Bundle.STOPPING:
			return "Stopping";
		case Bundle.UNINSTALLED:
			return "Uninstalled";
		case 0:
			return "Install";
		default:
			throw new IllegalStateException("Invalid state");
		}
	}

	public boolean isAllowedAction(BundleInfo bundle, String val) {
		int state = -1;
		try {
			state = bundle.getState();
		} catch (Exception e) {
			addActionError("Unable to determine bundle state: "
					+ e.getMessage());
			return false;
		}

		if ("start".equals(val)) {
			return state == Bundle.RESOLVED ||state == Bundle.INSTALLED;
		} else if ("stop".equals(val)) {
			return state == Bundle.ACTIVE;
		} else if ("update".equals(val)) {
			return state == Bundle.ACTIVE || state == Bundle.INSTALLED
					|| state == Bundle.RESOLVED;
		} else if ("install".equals(val)) {
			return state == 0;
		} else if ("delete".equals(val)) {
			return state == 0;
		} else if ("uninstall".equals(val)) {
			return state == Bundle.INSTALLED
					|| state == Bundle.RESOLVED;
		}
		throw new IllegalArgumentException("Invalid state");
	}

	public String install() throws MalformedURLException {
		log.debug("installing");
		BundleInfo info = service.loadMaxVersionBySymbolicName(symbolicName);
		String path = ServletActionContext.getServletContext().getRealPath(
				"/temp/");
		File file = new File(path + File.separator + info.getBundleFilename());
		FileUtils.byteToFile(info.getContent(), file);
		try {
			Bundle b = osgiHost.getBundleContext().installBundle(
					file.toURI().toURL().toString());
			if (b != null) {
				b.start();
			}
		} catch (BundleException e) {
			addActionError(e.getMessage());
		}
		log.debug("installed");
		return view();
	}

	public boolean isAllowedAction(Bundle bundle, String val) {
		int state = -1;
		try {
			state = bundle.getState();
		} catch (Exception e) {
			addActionError("Unable to determine bundle state: "
					+ e.getMessage());
			return false;
		}

		if ("start".equals(val)) {
			return state == Bundle.RESOLVED;
		} else if ("stop".equals(val)) {
			return state == Bundle.ACTIVE;
		} else if ("update".equals(val)) {
			return state == Bundle.ACTIVE || state == Bundle.INSTALLED
					|| state == Bundle.RESOLVED;
		}
		throw new IllegalArgumentException("Invalid state");
	}

	@Inject
	public void setConfiguration(Configuration configuration) {
		this.configuration = configuration;
	}

	@Inject
	public void setBundleAccessor(BundleAccessor bundleAccessor) {
		this.bundleAccessor = bundleAccessor;
	}

	public void setServletContext(ServletContext servletContext) {
		osgiHost = (OsgiHost) servletContext
				.getAttribute(StrutsOsgiListener.OSGI_HOST);
	}

	public String getSymbolicName() {
		return symbolicName;
	}

	public void setSymbolicName(String symbolicName) {
		this.symbolicName = symbolicName;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	@Override
	public void setServices(List<IBundleService> services) {
		if(services!=null){
			service=services.get(0);
		}
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		this.file = file;
	}

	public String getFileFileName() {
		return fileFileName;
	}

	public void setFileFileName(String fileFileName) {
		this.fileFileName = fileFileName;
	}

	public BundleInfo getInfo() {
		return info;
	}

	public void setInfo(BundleInfo info) {
		this.info = info;
	}
}
